/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethod6MLD.h"

//---------------------------------------------------------------------------

ScanMethod6MLD::ScanMethod6MLD(void)
{
	myType = __wmt6MLD;

	mCastAddr.Initialize();
}

ScanMethod6MLD::~ScanMethod6MLD(void)
{
}

//---------------------------------------------------------------------------
int ScanMethod6MLD::InternalInit(void)
{
	CString str;
	bool res;

	if(workPhase!=__wptDiscovery) return MSR_ERR_PHASE;

	mcat = __mcatUnknown;

	str = scanParameters.Trim();
	if(str.IsEmpty()) mcat = __mcatAllNodes_LinkLocal;
	else{
		if(str.CompareNoCase(_T("NN"))==0) mcat = __mcatAllNodes_NodeLocal;
		if(str.CompareNoCase(_T("NL"))==0) mcat = __mcatAllNodes_LinkLocal;
		if(str.CompareNoCase(_T("RN"))==0) mcat = __mcatAllRouters_NodeLocal;
		if(str.CompareNoCase(_T("RL"))==0) mcat = __mcatAllRouters_LinkLocal;
		if(str.CompareNoCase(_T("RS"))==0) mcat = __mcatAllRouters_SiteLocal;

		if(mcat==__mcatUnknown){
			res = mCastAddr.SetS(str);
			if(res)
				mcat = __mcatUserSpecified;
		}
	}

	if(mcat==__mcatUnknown)
		return MSR_ERR_PARAMETER;

	return MSR_ERR_OK;
}

int ScanMethod6MLD::InternalExecute(void)
{
	int oldMaxWaitTime;
	ScanPacket* pack;
	bool res;

	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	// save xMaxWaitTime because this method MUST wait as specified in RFC ...
	oldMaxWaitTime = config->xMaxWaitTime;
	// ... and modify it
	config->xMaxWaitTime = 10000;

	pack = FindFirstUnusedPacket();
	while(pack==NULL){
		SleepEx(waitTime, TRUE);
		CheckResponses();
		CheckDeaths();
		pack = FindFirstUnusedPacket();
	}

	if(pack!=NULL){
		pack->Initialize(true);
		pack->sTime       = GetTickCount();
		pack->addr        = 0;
		pack->port        = 0;
		pack->methodType  = myType;
		pack->fullAddress = NULL;
		pack->machineInfo = NULL;

		res = SendIPv6MLD(pack);
		if(!res)
			pack->Initialize(false);
	}
	else res = false;

	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	// restore xMaxWaitTime
	config->xMaxWaitTime = oldMaxWaitTime;

	if(hWnd!=NULL)
		PostMessage(hWnd, CHS_MSG_PACKETS_SENT, (WPARAM)0, (LPARAM)packSent);

	if(!res) return MSR_ERR_FAILED;

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

bool ScanMethod6MLD::SendIPv6MLD(ScanPacket* packIn)
{
	BYTE srcMAC[6];
	in_addr6 dstIPv6;
	in_addr6 srcIPv6;

	if(packIn==NULL) return false;

	winPcap->GetIPv6UnicastAddress(&srcIPv6);

	if(mcat==__mcatUserSpecified){
		mCastAddr.Get(&dstIPv6);
		if(mCastAddr.IsEmpty())
			return false;
	}
	else{
		if(!packetBuilderEx6->GetIPv6MulticastAddress(mcat, &dstIPv6))
			return false;
	}

	/*
	General query :
		dstIPv6 MUST be FF02:1
		hop limit MUST be 1
		DONE: multicast address field = 0
		DONE: IPv6 Router Alert option [RFC 2711] in a Hop-by-Hop Options header

	Specific query:
		dstIPv6 MUST be specified multicast address
		hop limit MUST be 1
		DONE: multicast address field = specified multicast address
		DONE: IPv6 Router Alert option [RFC 2711] in a Hop-by-Hop Options header
	*/

	
	if(!packetBuilderEx6->BuildPacketICMPMLDQuery(&srcIPv6, &dstIPv6, 0, 0, 1, 10000))
		return false;

	winPcap->FillSrcMAC(&srcMAC[0]);

	if(!packetBuilderEx6->AddEthernetHeaderForIPMulticast(srcMAC, &dstIPv6))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

//--------------------------------------------------------------------

void ScanMethod6MLD::ProcessMulticastListenerReport(BYTE* dataIn, int sizeIn, BYTE* srcIP, BYTE* srcMAC)
{
	ScanPacket   *pack;
//	ICMP6MLD     *mldh;
	MMachineInfo mach;
	MFullAddress addr;
	in_addr6 ia;
	int i;

	if(sizeIn<sizeof(ICMP6MLD)) return;

	if(!(receivedHeaders & RCV_HDR_HOPBYHOP))
		return;

	pack = FindPacket(0, 0, myType);
	if(pack==NULL) return;

//	mldh = (ICMP6MLD*)dataIn;

	// add address srcIP:srcMAC

	mach.Initialize();
	addr.Initialize();

	for(i=0; i<6; i++)
		addr.addrMAC.addr[i] = srcMAC[i];

	memcpy(ia.u.Byte, srcIP, 16);
	addr.addrIPv6.Set(&ia);

	mach.address.LoadFromPointer(&addr);

	machInfoList->AddIPv6(&mach);
}

//---------------------------------------------------------------------------
